package com.rent.interceptor;

import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSONObject;
import com.rent.annotation.NoAuth;
import com.rent.beans.Result;
import com.rent.beans.vo.LoginUser;
import com.rent.constants.SysConstant;
import com.rent.entity.WechatUser;
import com.rent.enums.ErrorEnum;
import com.rent.enums.UserStatusEnum;
import com.rent.service.WechatUserService;
import com.rent.utils.JwtUtil;
import com.rent.utils.RentSecurityManager;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.Objects;

/**
 * 认证拦截器
 * @author lihaoyang
 * @date 2021/1/22
 */
@Slf4j
@Component
public class LoginInterceptorHandler extends HandlerInterceptorAdapter {

//
//    @Autowired
//    RedisUtil redisUtil;

    @Autowired
    WechatUserService wechatUserService;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        //设置服务器端的编码
        response.setCharacterEncoding("utf-8");
        //通知浏览器服务器发送的数据格式
        response.setContentType("application/json;charset=utf-8");

        String token = request.getHeader(SysConstant.TOKEN);
        log.info("+++++LoginInterceptor, jwt token= [{}]",token);
        //如果不是映射到Controller方法直接放行
        if(!(handler instanceof HandlerMethod)){
            return true;
        }
        HandlerMethod handlerMethod = (HandlerMethod)handler;
        Method method = handlerMethod.getMethod();
        //有NoAuth注解不需要登录
        if(method.isAnnotationPresent(NoAuth.class)){
            return true;
        }


        if(StringUtils.isBlank(token)){
            log.warn("token 为空 token={}",token);
            response.getWriter().write(JSONObject.toJSONString(Result.error(ErrorEnum.TOKEN_NULL)));
            return false;
        }
        String openId = JwtUtil.getOpenId(token);
        if(Objects.isNull(openId)){
            log.warn("openId 为空 token={}",token);
            response.getWriter().write(JSONObject.toJSONString(Result.error(ErrorEnum.TOKEN_PARSE_ERROR)));
            return false;
        }
        if(!JwtUtil.verify(token)){
            log.warn("token 解析失败 token={}",token);
            response.getWriter().write(JSONObject.toJSONString(Result.error(ErrorEnum.TOKEN_EXPIRED)));
            return false;
        }
        //根据userId从缓存获取获取用户信息
        WechatUser wechatUser = wechatUserService.getByOpenId(openId);
        log.error("错误标记1", JSONUtil.toJsonStr(wechatUser));
        if(wechatUser==null){
            wechatUser=new WechatUser();
            wechatUser.setOpenId(openId);
        }
        if(wechatUser == null){
            response.getWriter().write(JSONObject.toJSONString(Result.error(ErrorEnum.USER_NOT_EXIST)));
            return false;
        }
        //判断用户状态
        if(UserStatusEnum.LOCK.getCode().equals(wechatUser.getStatus())){
            response.getWriter().write(JSONObject.toJSONString(Result.error(ErrorEnum.USER_LOCKED)));
            return false;
        }
        // 校验token是否超时失效
        if (!JwtUtil.verify(token)) {
            response.getWriter().write(JSONObject.toJSONString(Result.error(ErrorEnum.TOKEN_EXPIRED)));
            return false;
        }
        //登录用户信息放入ThreadLocal
        RentSecurityManager.setLoginUser(new LoginUser(wechatUser));
        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        //防止内存移出
        RentSecurityManager.removeLoginUser();
    }

    /**
     * JWTToken刷新生命周期 （实现： 用户在线操作不掉线功能）
     * 1、登录成功后将用户的JWT生成的Token作为k、v存储到cache缓存里面(这时候k、v值一样)，缓存有效期设置为Jwt有效时间的2倍
     * 2、当该用户再次请求时，通过JWTFilter层层校验之后会进入到doGetAuthenticationInfo进行身份验证
     * 3、当该用户这次请求jwt生成的token值已经超时，但该token对应cache中的k还是存在，则表示该用户一直在操作只是JWT的token失效了，程序会给token对应的k映射的v值重新生成JWTToken并覆盖v值，该缓存生命周期重新计算
     * 4、当该用户这次请求jwt在生成的token值已经超时，并在cache中不存在对应的k，则表示该用户账户空闲超时，返回用户信息已失效，请重新登录。
     * 注意： 前端请求Header中设置Authorization保持不变，校验有效性以缓存中的token为准。
     *       用户过期时间 = Jwt有效时间 * 2。
     *
     * @param token
     * @param userId
     * @return
     */
//    public boolean jwtTokenRefresh(String token, String userId) {
//        String cacheToken = String.valueOf(redisUtil.get(RedisConstant.PREFIX_USER_TOKEN + token));
//        if (StringUtils.isBlank(cacheToken)) {
//            log.info("redis中不存在token:{}", cacheToken);
//            return false;
//        }
//        // 校验token有效性
//        if (!JwtUtil.verify(cacheToken)) {
//            String newJwt = JwtUtil.getJWT(userId);
//            // 设置超时时间
//            //token放缓存
//            redisUtil.set(RedisConstant.PREFIX_USER_TOKEN + newJwt, newJwt, JwtUtil.EXPIRE_TIME * 2 / 1000);
//            log.info("——————————用户在线操作，更新token保证不掉线—————————jwtTokenRefresh——————— " + token);
//            return true;
//        }
//        return true;
//    }
}
